﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Composition;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Unread.App.Common;
using Unread.App.Services.Interfaces;
using Unread.App.Views;
using Unread.Business.Models;
using Unread.Business.Repositories.Interfaces;
using Unread.Business.Services.Interfaces;
using Unread.Notifications.Models;
using Unread.Notifications.Services.Interfaces;

namespace Unread.App.ViewModels
{
    [Export]
    public class UnreadViewModel : BindableBase
    {
        private readonly IFeedRepository _feedRepository;
        private readonly IUnreadFeedItemLoader _unreadFeedItemLoader;
        private readonly INavigationService _navigationService;
        private readonly IFeedReadMarker _feedReadMarker;
        private readonly ITileUpdater _tileNotifier;
        private readonly IToastNotifier _toastNotifier;

        private bool _isLoading;
        public bool IsLoading
        {
            get { return _isLoading; }
            set 
            { 
                SetProperty(ref _isLoading, value); 
                (RefreshFeedsCommand as DelegateCommand).RaiseCanExecuteChanged(); 
            }
        }

        private bool _isAllRead;
        public bool IsAllRead
        {
            get { return _isAllRead; }
            set { SetProperty(ref _isAllRead, value); }
        }

        private UnreadFeedItemViewModel _selectedFeedItem;
        public UnreadFeedItemViewModel SelectedFeedItem
        {
            get { return _selectedFeedItem; }
            set { SetProperty(ref _selectedFeedItem, value); }
        }

        public ObservableCollection<UnreadFeedViewModel> Feeds { get; set; }

        [ImportingConstructor]
        public UnreadViewModel(IUnreadFeedItemLoader unreadFeedItemLoader, IFeedRepository feedRepository, INavigationService navigationService,
            IFeedReadMarker feedReadMarker, ITileUpdater tileNotifier, IToastNotifier toastNotifier)
        {
            if (unreadFeedItemLoader == null) throw new ArgumentNullException("unreadFeedItemLoader");
            if (feedRepository == null) throw new ArgumentNullException("feedRepository");
            if (navigationService == null) throw new ArgumentNullException("navigationService");
            if (feedReadMarker == null) throw new ArgumentNullException("feedReadMarker");
            if (tileNotifier == null) throw new ArgumentNullException("tileNotifier");
            if (toastNotifier == null) throw new ArgumentNullException("toastNotifier");

            _unreadFeedItemLoader = unreadFeedItemLoader;
            _feedRepository = feedRepository;
            _navigationService = navigationService;
            _feedReadMarker = feedReadMarker;
            _tileNotifier = tileNotifier;
            _toastNotifier = toastNotifier;

            Feeds = new ObservableCollection<UnreadFeedViewModel>();

            RefreshFeedsCommand = new DelegateCommand(RefreshFeeds, CanRefreshFeeds);
            AddFeedCommand = new DelegateCommand(AddFeed, CanAddFeed);
            ViewAllFeedsCommand = new DelegateCommand(ViewAllFeeds, CanViewAllFeeds);

            PropertyChanged += GroupedItemsViewModel_PropertyChanged;
        }

        public async Task Load()
        {
            IsLoading = true;

            await LoadFeeds();

            IsLoading = false;
            IsAllRead = (!Feeds.Any());

            SendNotifications();
        }

        private async Task LoadFeeds()
        {
            var allFeeds = await _feedRepository.GetAll();
            foreach (var feed in allFeeds)
            {
                try
                {
                    await LoadUnreadItems(feed);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                }
            }
        }

        private void SendNotifications()
        {
            var totalUnreadItems = Feeds.Sum(f => f.UnreadItems.Count);

            UpdateTile(totalUnreadItems);

            if (!IsAllRead)
                _toastNotifier.Notify(totalUnreadItems, TimeSpan.FromSeconds(2));
        }

        private void UpdateTile(int totalUnreadItems)
        {
            var request = new TileUpdateRequest { LargeText = totalUnreadItems.ToString(), RegularText = "Unread", Delay = TimeSpan.FromSeconds(4) };
            _tileNotifier.Update(request);
        }

        private async Task LoadUnreadItems(Feed feed)
        {
            var unreadItems = await _unreadFeedItemLoader.Load(feed.Id);

            if (unreadItems.Any())
            {
                var unreadFeed = new UnreadFeedViewModel(_navigationService)
                {
                    FeedId = feed.Id,
                    Title = feed.Title
                };

                foreach (var item in unreadItems)
                {
                    unreadFeed.UnreadItems.Add(new UnreadFeedItemViewModel
                    {
                        FeedId = unreadFeed.FeedId,
                        FeedItemId = item.Id,
                        Title = item.Title,
                        Subtitle = item.Summary,
                        Link = item.Link,
                        Content = item.Summary,
                        PublishDate = item.PublishDate
                    });
                }

                Feeds.Add(unreadFeed);
            }

            await _feedReadMarker.MarkAsRead(feed.Id);
        }

        #region RefreshFeedsCommand

        public ICommand RefreshFeedsCommand { get; set; }

        private bool CanRefreshFeeds(object arg)
        {
            return !IsLoading;
        }

        private async void RefreshFeeds(object obj)
        {
            Feeds.Clear();
            await Load();
        }

        #endregion

        #region AddFeedCommand

        public ICommand AddFeedCommand { get; set; }

        private bool CanAddFeed(object arg)
        {
            return true;
        }

        private void AddFeed(object obj)
        {
            _navigationService.Navigate(typeof(AddFeedPage));
        }

        #endregion

        #region OpenFeedItemCommand

        private bool CanOpenFeedItem(object arg)
        {
            return (arg as UnreadFeedItemViewModel != null);
        }

        private void OpenFeedItem(object obj)
        {
            var feedItem = ((UnreadFeedItemViewModel)obj);
            var navParams = new FeedItemIdentifier { FeedId = feedItem.FeedId, FeedItemUri = feedItem.Link };
            _navigationService.Navigate(typeof(FeedItemPage), navParams);
        }

        private void GroupedItemsViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "SelectedFeedItem")
                if (CanOpenFeedItem(_selectedFeedItem))
                    OpenFeedItem(_selectedFeedItem);
        }

        #endregion

        #region ViewAllFeedsCommand

        public ICommand ViewAllFeedsCommand { get; set; }

        private bool CanViewAllFeeds(object arg)
        {
            return true;
        }

        private void ViewAllFeeds(object obj)
        {
            _navigationService.Navigate(typeof(ManageFeedsPage));
        }

        #endregion
    }
}